package com.ruoyi.web.controller.tool;
import com.google.protobuf.Any;
import com.google.protobuf.InvalidProtocolBufferException;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.web.easytron.BaseTest;
import com.ruoyi.web.easytron.Exception.FunctionSelectorException;
import com.ruoyi.web.easytron.Exception.SmartParamDecodeException;
import com.ruoyi.web.easytron.model.Transfer;
import com.ruoyi.web.easytron.model.Trc20ContractInfo;
import com.ruoyi.web.easytron.util.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.web.bind.annotation.*;
import org.tron.trident.core.ApiWrapper;
import org.tron.trident.core.Constant;
import org.tron.trident.core.contract.Trc20Contract;
import org.tron.trident.core.exceptions.IllegalException;
import org.tron.trident.core.key.KeyPair;

import org.tron.trident.proto.Chain;
import org.tron.trident.proto.Contract;
import org.tron.trident.proto.Response;
import org.tron.trident.utils.Convert;

import org.web3j.crypto.Bip32ECKeyPair;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.MnemonicUtils;

@Api("波场测试")
@RestController
@RequestMapping("/test/tron")
public class TronUnitController  extends BaseController {


    /**
     * # ApiWrapper原装 - 获取trx余额
     */
    @Anonymous
    @ApiOperation("获取trx余额")
    @ApiImplicitParam(name = "frokey", value = "用户私钥", required = true, dataType = "string", paramType = "path", dataTypeClass = String.class)
    @GetMapping("/balanceoftrx")
    public R<String> simple_balanceOfTrx(String frokey) {
        if(!frokey.isEmpty()&&frokey.length()>2) {
            // 地址
            BaseTest.key = frokey;
        }
        // 获取账户信息
        Response.Account account = BaseTest.wrapper.getAccount(BaseTest.from);
        // 余额
        long balance = account.getBalance();
        // 真实余额
        BigDecimal trx = Convert.fromSun(new BigDecimal(balance), Convert.Unit.TRX);

        System.out.println("------------trx余额："+trx.toString()+"个");

        long sum = account.getFrozenList().stream().mapToLong(Response.Account.Frozen::getFrozenBalance).sum();
        long frozenBalance = account.getAccountResource().getFrozenBalanceForEnergy().getFrozenBalance();
        long frozen = sum + frozenBalance;
        BigDecimal frozenTrx = Convert.fromSun(new BigDecimal(frozen), Convert.Unit.TRX);

        System.out.println("------------trx质押："+frozenTrx+"个");
        System.out.println("------------trx 总余额："+trx.add(frozenTrx)+"个");
        return R.ok(frozenTrx.toString());
    }

    /**
     * trx 转账 - trident原生 - 非本地构造交易
     */
    @ApiOperation("trx 转账")
    @Anonymous
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fromkey", value = "发送地址", dataType = "String", dataTypeClass = String.class),
            @ApiImplicitParam(name = "tokey", value = "接收地址", dataType = "String", dataTypeClass = String.class),
            @ApiImplicitParam(name = "trxnum", value = "数量", dataType = "Integer", dataTypeClass = Integer.class)
    })
    @GetMapping("/trident")
    public R<String> transferTrx_trident(TronUnitEntiy trxentity) {
        BaseTest.from=trxentity.getfromkey();
        BaseTest.to=trxentity.gettokey();
        // trx 个数
        BigDecimal realAmount = BigDecimal.valueOf(trxentity.gettrxnum());
        // sun 个数
        BigDecimal sun = Convert.toSun(realAmount, Convert.Unit.TRX);

        // 远程构造交易
        Response.TransactionExtention transfer = null;
        try {
            transfer = BaseTest.wrapper.transfer(BaseTest.from, BaseTest.to, sun.longValue());
        } catch (IllegalException e) {
            throw new RuntimeException(e);
        }
        // 签名
        Chain.Transaction signTransaction = BaseTest.wrapper.signTransaction(transfer);
        // 广播
        String tid = BaseTest.wrapper.broadcastTransaction(signTransaction);
        System.out.println("------------广播："+tid);
        return R.ok(tid.toString());
    }
    @SneakyThrows
    @Anonymous
    @ApiOperation("trx根据私钥 查询交易")
    @ApiImplicitParam(name = "frokey", value = "用户私钥", required = true, dataType = "string", paramType = "path", dataTypeClass = String.class)
    @GetMapping ("/balancereadtrx")
    public R<String> blockReadTest_trident(String frokey) {
        BaseTest.key=frokey;
        //测试网读取区块看不出效果，交易数量太少
        BaseTest.wrapper = new ApiWrapper(Constant.FULLNODE_NILE, Constant.FULLNODE_NILE_SOLIDITY, BaseTest.key);
        // 获取最新区块
        Chain.Block nowBlock = BaseTest.wrapper.getNowBlock();
        // 区块ID
        String blockId = BlockUtil.parseBlockId(nowBlock);
        String returnStr="";
        returnStr="区块ID："+ blockId;
        System.out.println("区块ID："+ blockId);
        returnStr="<br/>"+returnStr+"区块高度：{}"+ nowBlock.getBlockHeader().getRawData().getNumber()+"<br/>";
        System.out.println("区块高度：{}"+ nowBlock.getBlockHeader().getRawData().getNumber());
        if (nowBlock.getTransactionsCount() <= 0) {
            returnStr="<br/>"+returnStr+"交易数量为0";
            System.out.println("交易数量为0");
            return R.fail("交易数量为0");
        }
        // 区块中的所有交易
        List<Chain.Transaction> transactionsList = nowBlock.getTransactionsList();
        // 遍历
        for (Chain.Transaction transaction : transactionsList) {
            System.out.println("--------------------------------");
            // 交易ID
            String transactionId = TransactionUtil.getTransactionId(transaction);
            returnStr="<br/>"+returnStr+"交易ID：{}" + transactionId;
            System.out.println("交易ID：{}" + transactionId);
            // 交易状态
            boolean status = transaction.getRet(0).getContractRet().getNumber() == 1;
            returnStr="<br/>"+returnStr+"交易状态：" + status + "(成功:失败)";
            System.out.println("交易状态：" + status + "(成功:失败)");
            if (!status) {
                continue;
            }
            // 合约
            Chain.Transaction.Contract contract = transaction.getRawData().getContract(0);
            // 合约类型
            Chain.Transaction.Contract.ContractType contractType = contract.getType();
            // parameter - 数据|入参
            Any parameter = contract.getParameter();
            // 根据合约类型使用不同的工具进行解码
            // 如果是 触发智能合约 操作
            if (contractType == Chain.Transaction.Contract.ContractType.TriggerSmartContract) {
                try {
                    returnStr="<br/>"+returnStr+"这是智能合约交易";
                    System.out.println("这是智能合约交易");
                    // 解码
                    Contract.TriggerSmartContract triggerSmartContract =
                            parameter.unpack(Contract.TriggerSmartContract.class);
                    // 获取交易详情
                    Transfer transfer = TransferUtil.getTransferInfo(triggerSmartContract);
                    returnStr="<br/>"+returnStr+"类型：" + transfer.getTransferType() + "\t到账地址：" + transfer.getTo() + "\t金额：{}" + transfer.getAmount();
                    System.out.println("类型：" + transfer.getTransferType() + "\t到账地址：" + transfer.getTo() + "\t金额：{}" + transfer.getAmount());
                    // ......
                } catch (InvalidProtocolBufferException e) {
                    returnStr="<br/>"+returnStr+"unpack解包异常";
                    System.out.println("unpack解包异常");
                    e.printStackTrace();
                } catch (SmartParamDecodeException e) {
                    returnStr="<br/>"+returnStr+"智能合约 转账参数 数据解析异常";
                    System.out.println("智能合约 转账参数 数据解析异常");
                    e.printStackTrace();
                } catch (FunctionSelectorException e) {
                    // 函数选择器错误
                } catch (Exception e) {
                    returnStr="<br/>"+returnStr+"兜底异常：{}" + e.getMessage();
                    System.out.println("兜底异常：{}" + e.getMessage());
                    e.printStackTrace();
                }
            }
            // 如果是trx
            else if (contractType == Chain.Transaction.Contract.ContractType.TransferContract) {
                try {
                    returnStr="<br/>"+returnStr+"这是TRX交易";
                    System.out.println("这是TRX交易");
                    Contract.TransferContract unpack = parameter.unpack(Contract.TransferContract.class);
                    Transfer transferInfo = TransferUtil.getTransferInfo(unpack);
                } catch (InvalidProtocolBufferException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return R.ok(returnStr);
    }
    //Tron波场区块链 | 使用Java将Tron钱包助记词转私钥 全网独门一份
//https://blog.csdn.net/t610654893/article/details/137183319?ops_request_misc=&request_id=&biz_id=102&utm_term=java%20%20Tron&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-4-137183319.142^v100^pc_search_result_base2&spm=1018.2226.3001.4449


/****
 * 更加12位助记词生成地址，私钥，公钥**/

    @Anonymous
    @ApiOperation("根据助记词生成私钥，公钥，地址")
    @ApiImplicitParam(name = "str", value = "用户私钥", required = true, dataType = "string", paramType = "path", dataTypeClass = String.class)
    @GetMapping("/troncreate")
     public R<String>   createPPKID(String str) {
         int HARDENED_BIT = 0x80000000;

         String mnemonic = str;//"program repair next claw rival slight spider tennis begin cute daring fancy";
       byte[] seed = MnemonicUtils.generateSeed(mnemonic, "");
         Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed);
         // m/44'/195'/0'/0/0
         final int[] path = {44 | HARDENED_BIT, 195 | HARDENED_BIT, 0 | HARDENED_BIT, 0, 0};
         Bip32ECKeyPair bip44Keypair = Bip32ECKeyPair.deriveKeyPair(masterKeypair, path);
        Credentials credentials = Credentials.create(bip44Keypair);
         String privateKey = credentials.getEcKeyPair().getPrivateKey().toString(16);
         System.out.println(privateKey);

         KeyPair keyPair = new KeyPair(privateKey);
         String base58CheckAddress = keyPair.toBase58CheckAddress();
         String publicKey = keyPair.toPublicKey();
         System.out.println(publicKey);
         System.out.println(base58CheckAddress); /*  */
        return R.ok("地址："+base58CheckAddress+"  >>>>>私钥:"+privateKey+"  >>>>>公钥"+publicKey);
     }
}

class TronUnitEntiy{
    public String from_key;

    public String to_key;

    public Integer trx_num;
    public TronUnitEntiy()
    {

    }

    public TronUnitEntiy(String fromkey, String tokey, Integer trxnum)
    {
        this.from_key = fromkey;
        this.to_key = tokey;
        this.trx_num = trxnum;
    }

    public String getfromkey()
    {
        return from_key;
    }

    public void setfromkey(Integer userId)
    {
        this.from_key = from_key;
    }
    public String gettokey()
    {
        return to_key;
    }

    public void settokey(Integer userId)
    {
        this.to_key = to_key;
    }
    public Integer gettrxnum()
    {
        return trx_num;
    }

    public void settrxnum(Integer userId)
    {
        this.trx_num = trx_num;
    }


}
